
#include "lib.h"

#include <stdlib.h>
#include "SevenCardStudGame.h"
#include "vpconn.h"

#include "SevenCardStudSQL.h"
#include "SevenCardStudRanks.h"

#include "FiveCardHand.h"
#include "stdncerr.h"

#include <math.h>


// pay tables used for payouts on the main game
const int32 SevenCardStudGame::m_anPayTable[9][11] = 
{
    { 0,   0,   1,   2,   3,   4,   5,  50, 100,    0,  500 },  // no joker, low
    { 0,   0,   1,   2,   3,   4,   6,  45, 100,    0,  500 },  // no joker, med
    { 0,   0,   1,   2,   3,   4,   6,  50, 125,    0,  500 },  // no joker, high
    { 0,   0,   0,   1,   2,   3,   4,  12,  50,   75,  100 },  // 1 joker, low
    { 0,   0,   0,   1,   2,   3,   5,  10,  50,   75,  100 },  // 1 joker, med
    { 0,   0,   0,   1,   2,   3,   4,  15,  50,  100,  125 },  // 1 joker, high
    { 0,   0,   0,   1,   2,   2,   3,   5,  12,   20,   25 },  // 2 jokers, low
    { 0,   0,   0,   1,   2,   2,   3,   5,  15,   20,   25 },  // 2 jokers, med
    { 0,   0,   0,   1,   2,   2,   3,   6,  12,   20,   25 }   // 2 jokers, high
};


// pay tables used for payouts on sidebets
const int32 SevenCardStudGame::m_anSideBetPayTable[9][11] = 
{
    { 0,  0,  0,  0,  0,  0,  0,  750, 1500, 0, 5000 },   // no joker, low
    { 0,  0,  0,  0,  0,  0,  0,  775, 1500, 0, 5250 },   // no joker, med
    { 0,  0,  0,  0,  0,  0,  0,  800, 1500, 0, 5500 },   // no joker, high
    { 0,  0,  0,  0,  0,  0,  0,  750, 1750, 0, 5750 },   // 1 joker, low
    { 0,  0,  0,  0,  0,  0,  0,  775, 2000, 0, 5750 },   // 1 joker, med
    { 0,  0,  0,  0,  0,  0,  0,  800, 2000, 0, 6000 },   // 1 joker, high
    { 0,  0,  0,  0,  0,  0,  0,  900, 2000, 0, 5800 },   // 2 jokers, low
    { 0,  0,  0,  0,  0,  0,  0, 1000, 1850, 0, 6000 },   // 2 jokers, med
    { 0,  0,  0,  0,  0,  0,  0, 1075, 1900, 0, 6000 }    // 2 jokers, high
};


// number of cards for the 3 supported deck configurations
const int32 SevenCardStudGame::m_anDeckSize[3] = { 52, 53, 54 };



//////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn SevenCardStudGame::SevenCardStudGame(VPConnection* pConn, GameDBInterface *pDBInterface)
///
/// Constructor used for NEW games.
///
/// @param pConn        -   (in)  connection to the client
/// @param pDBInterface -   (in)  connection to the database
///
//////////////////////////////////////////////////////////////////////////////////////////////////
SevenCardStudGame::SevenCardStudGame(VPConnection* pConn, GameDBInterface *pDBInterface) :
                    m_hand(&m_jokerCardMgr),
                    m_bestHand(&m_stdCardMgr),
                    m_dblUpHand(&m_stdCardMgr)
{
    m_pConn     = pConn;    
    m_pDBInterface = (SevenCardStudSQL *) pDBInterface;

    // get configuration settings
    m_ucPayTable    = (uint8) m_pConn->getPayTableFreq();
    m_bDblUpEnabled = m_pConn->getDoubleUpEnabled();
	m_nDlbUpCapAmount = m_pConn->getDoubleUpCapAmount();

    m_ucDeckConfiguration = m_pConn->getDeckConfiguration();
    m_jokerCardMgr.setDeckSize(m_anDeckSize[m_ucDeckConfiguration]);

	m_sgi.gameId = m_pConn->getGameID();
	m_sgi.machId = m_pConn->getMachId();

    m_ucSideBetPayTable = m_ucPayTable;
    m_bSideBetEnabled   = m_pConn->getSideBetEnabled();

    // init values
    m_eState    = VPGS_NOTSTARTED;

    m_nPhase    = 0;
    m_nPayout   = 0;
    m_nBalance  = 0;
    m_nTotalBet = 0;

    m_nBestHandRank = HR_HIGHCARD;

    m_nBet   = 0;
    m_nBet_2 = 0;
    m_nBet_3 = 0;
    m_nBet_4 = 0;

    m_ucDblUpCard   = 0;
    m_nDblUpPayout  = 0;
    m_nDblUpPayType = 0;
    m_nDblUpWon     = 0;
    m_nDblUpRound   = 0;
    m_nDblUpRank    = 0;

    m_nSideBet        =  0;
    m_nSideBetPayout  =  0;
    m_nSideBetWinType = -1;
}


//////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn SevenCardStudGame::SevenCardStudGame(VPConnection* pConn, GameDBInterface *pDBInterface, 
///                                          SevenCardSavedGame *pSave)
///
/// Constructor used for restoring saved games.  This should restore the game to where it left off.
///
/// @param pConn        -   (in)  connection to the client
/// @param pDBInterface -   (in)  connection to the database
/// @param pSave        -   (in)  structure holding the saved game state information 
///                               needed to restore the game.
///
//////////////////////////////////////////////////////////////////////////////////////////////////
SevenCardStudGame::SevenCardStudGame(VPConnection* pConn, GameDBInterface *pDBInterface, SevenCardSavedGame *pSave) :
                    m_hand(&m_jokerCardMgr),
                    m_bestHand(&m_stdCardMgr),
                    m_dblUpHand(&m_stdCardMgr)
{
    m_pConn = pConn;

    m_pDBInterface = (SevenCardStudSQL *) pDBInterface;

    m_nBestHandRank = HR_HIGHCARD;
    memset(m_aucBestCards, 0, sizeof(m_aucBestCards));

    m_ucPayTable = pSave->ucPayTable;    

    m_nPhase = pSave->ucPhase==0?1:pSave->ucPhase;

    m_nBet   = m_pConn->centsToCredits(pSave->nBet);
    m_nBet_2 = m_pConn->centsToCredits(pSave->nBet_2);
    m_nBet_3 = m_pConn->centsToCredits(pSave->nBet_3);
    m_nBet_4 = m_pConn->centsToCredits(pSave->nBet_4);

    m_nTotalBet = m_nBet + m_nBet_2 + m_nBet_3 + m_nBet_4;

    m_nPayout   = 0;
    m_nBalance  = 0;

    m_ucSideBetPayTable = pSave->ucSideBetPayTable;
    m_nSideBet          = m_pConn->centsToCredits(pSave->nSideBet);
    m_nSideBetPayout    = m_pConn->centsToCredits(pSave->nSideBetPayout);
    m_bSideBetEnabled   = pSave->bSideBetEnabled;
    m_ucSideBetRank     = pSave->ucSideBetRank;
    m_nSideBetWinType   = pSave->nSideBetWinType;

    m_ucDeckConfiguration   = pSave->ucDeckConfiguration;
    m_jokerCardMgr.setDeckSize(m_anDeckSize[m_ucDeckConfiguration]);

	m_sgi.gameId = m_pConn->getGameID();
	m_sgi.machId = m_pConn->getMachId();

    Shue shue(&m_jokerCardMgr, 1, m_sgi);
    shue.shuffle(pSave->shue.seed);

	m_hand.add(shue.getCard());
    m_hand.add(shue.getCard());
    m_hand.add(shue.getCard());
    m_hand.add(shue.getCard());
    m_hand.add(shue.getCard());
    m_hand.add(shue.getCard());
    m_hand.add(shue.getCard());

    memcpy(m_aucBestCards,pSave->aucBestCards, 5);

    m_ucDblUpCard   = 0;
    m_nDblUpRound   = 0;
    m_nDblUpRank    = 0;
    m_nDblUpPayout  = 0;
    m_nDblUpPayType = 0;
    m_nDblUpWon     = 0;
    m_bDblUpEnabled = pSave->ucDblUpEnabled;

	m_nDlbUpCapAmount = m_pConn->getDoubleUpCapAmount();

    switch(pSave->eKind)
    {
        case SevenCardSavedGame::VPOKER_PLAYING:
        {
            m_eState = VPGS_GAMESTARTED;

            break;
        }
        case SevenCardSavedGame::DBLUP_AVAIL_1ST:
        {
            m_eState        = VPGS_DBLUPPENDING;
            m_nPayout       = m_nDblUpPayout  = m_pConn->centsToCredits(pSave->nDblUpPayout);
            m_nBestHandRank = m_nDblUpPayType = pSave->nDblUpOrigPayType;

            break;
        }
        case SevenCardSavedGame::DBLUP_AVAIL_NOT1ST:
        {
            m_eState        = VPGS_DBLUPPENDING;
            m_nPayout       = m_nDblUpPayout  = m_pConn->centsToCredits(pSave->nDblUpPayout);
            m_nBestHandRank = m_nDblUpPayType = pSave->nDblUpOrigPayType;

            m_nDblUpRound = pSave->nDblUpRound;
            m_nDblUpWon   = pSave->nDblUpWins;

            Shue dblUpShue(&m_stdCardMgr, 1, m_sgi);
            dblUpShue.shuffle(pSave->dblUpShue.seed);

	        m_dblUpHand.add(dblUpShue.getCard());
            m_dblUpHand.add(dblUpShue.getCard());
            m_dblUpHand.add(dblUpShue.getCard());
            m_dblUpHand.add(dblUpShue.getCard());
            m_dblUpHand.add(dblUpShue.getCard());
            m_dblUpHand.add(dblUpShue.getCard());
            m_dblUpHand.add(dblUpShue.getCard());

            break;
        }
        case SevenCardSavedGame::DBLUP_PLAYING_NOT1ST:
        case SevenCardSavedGame::DBLUP_PLAYING:
        {
            m_eState        = VPGS_DBLUPSTARTED;
            m_nPayout       = m_nDblUpPayout = m_pConn->centsToCredits(pSave->nDblUpPayout);
            m_nBestHandRank = m_nDblUpPayType = pSave->nDblUpOrigPayType;

            m_nDblUpRound = pSave->nDblUpRound;
            m_nDblUpWon   = pSave->nDblUpWins;

            Shue dblUpShue(&m_stdCardMgr, 1, m_sgi);
            dblUpShue.shuffle(pSave->dblUpShue.seed);

            m_ucDblUpCard = dblUpShue.getCard();
            m_nDblUpRank  = m_stdCardMgr.getRank(m_ucDblUpCard);

		    m_dblUpHand.add(0xFF);
            m_dblUpHand.add(dblUpShue.getCard());
            m_dblUpHand.add(dblUpShue.getCard());
            m_dblUpHand.add(dblUpShue.getCard());
            m_dblUpHand.add(dblUpShue.getCard());
            m_dblUpHand.add(dblUpShue.getCard());
            m_dblUpHand.add(dblUpShue.getCard());

            break;
        }
    }        
}

////////////////////////////////////////////////////////////////////////////////////
/// @fn  SevenCardStudGame::~SevenCardStudGame()
///
/// Destructor.
////////////////////////////////////////////////////////////////////////////////////
SevenCardStudGame::~SevenCardStudGame()
{

}


////////////////////////////////////////////////////////////////////////////////////
/// @fn SevenCardStudGame::handleMessage(unsigned char *pucMsgBuf, int32 nMsgLen)
///
/// Any messages not handled by the connection object are handed to the game object
/// for possible processing.  So, look at the message buffer and process anything
/// that we recognize.
///
/// @param pucMsgBuf    -   (in)  the raw message data
/// @param nMsgLen      -   (in)  the size of the message
///
/// @return true if we process the message, false otherwise.
///
////////////////////////////////////////////////////////////////////////////////////
bool8 SevenCardStudGame::handleMessage(unsigned char *pucMsgBuf, int32 nMsgLen)
{
    bool8 bRetVal = true;

    switch (pucMsgBuf[0])
    {
        case 'D':   // the initial Deal message that starts a new game
        {
			// [AO 2008-02-13] trackValidateSession() handled in connection

            int nBet = (((int32) pucMsgBuf[1])&0xFF) +
                        (((int32)pucMsgBuf[2] << 8)&0xFF00) +
                        (((int32)pucMsgBuf[3] << 16)&0xFF0000);


            int32 nSideBet = (((int32) pucMsgBuf[4]) & 0xFF) +
                             (((int32) pucMsgBuf[5] << 8)&0xFF00) +
                             (((int32) pucMsgBuf[6] << 16)&0xFF0000);

            uint8 ucSideBetRank = pucMsgBuf[7];


            int nResult = deal(nBet, nSideBet, ucSideBetRank);

            if (nResult != DEAL_SUCCESS)
            {
                switch (nResult)
                {
                    case VPGameBase::DEAL_INVALID_BET:
                        m_pConn->Answer('B');
                        break;

                    case VPGameBase::DEAL_NOT_ENOUGH_MONEY:
                        m_pConn->Answer('$');
                        break;

                    case VPGameBase::DEAL_CREATE_GAME_FAILURE:
                        m_pConn->Answer('N');
                        break;

                    case VPGameBase::DEAL_PROGRESSIVE_FAILURE:
                        m_pConn->Error(0x05);
                        break;
                }

                m_pConn->gameOver();
            }


            break;
        }

        case '1':   // the user made the second or third bet and hit the "Deal" button
        {

            int nBet = (((int32) pucMsgBuf[1])&0xFF) +
                        (((int32)pucMsgBuf[2] << 8)&0xFF00) +
                        (((int32)pucMsgBuf[3] << 16)&0xFF0000);

            showNext(nBet);

            break;
        }

        case 'A':   // the user either made the fourth bet and hit "deal", or hit "Deal All".
        {

            int nBet = (((int32) pucMsgBuf[1])&0xFF) +
                        (((int32)pucMsgBuf[2] << 8)&0xFF00) +
                        (((int32)pucMsgBuf[3] << 16)&0xFF0000);

            showAll(nBet);
            
            if(m_eState == VPGS_GAMEOVER)
            {                
                // the user must not have qualified for a double up round...game over.
                m_pConn->gameOver();
            }

            break;
        }
        case 'U':   // the user hit the "Yes" button and agreed to play double or nothing
        {
            doubleUpDeal();

            break;
        }

        case 'u':   // the user made their double up choice...big or small.
        {
            int32 nResult = doubleUpSelect(pucMsgBuf[1]); 

            switch (nResult)
            {
                case DUDRAW_SUCCESS_GAMEOVER:
                {
                    // the user picked wrong...game over
                    m_pConn->gameOver();
                    break;
                }
                case DUDRAW_SUCCESS_DBLUP_PENDING:
                {
                    // the user picked right...they are now being offered another round
                    break;
                }
                case DUDRAW_FAILURE:
                default:
                {
                    // HMMMMMMMMMMMMM...error.
                    m_pConn->Error(LOGINERR_CONTACTCUSTSERVICE);
                    m_pConn->gameOver();
                    break;
                }
            }
            break;
        }

        case 'X':   // the user declined to play double or nothing.  They get to keep their money.
        {           // the game is over.
            doubleUpDeny();

            break;
        }
        default:
        {
            // we don't recognize this message...let the caller know.
            bRetVal = false;
            break;
        }
    }

    return bRetVal;
}



///////////////////////////////////////////////////////////////////////////////////////////////
/// @fn SevenCardStudGame::doubleUpDeny()
/// 
/// The user selected "No" when offered a double up round.  The game is over, the user keeps
/// their money.
///
/// @return true if all is well, false otherwise.
///////////////////////////////////////////////////////////////////////////////////////////////
bool8 SevenCardStudGame::doubleUpDeny()
{
    bool8 bResult = false;

    if (m_eState == VPGS_DBLUPPENDING)
    {
        m_eState = VPGS_GAMEOVER;

        if (m_pDBInterface->trackDoubleUpDeny(m_pConn->creditsToCents(m_nDblUpPayout),
                                              m_pConn->creditsToCents(m_nSideBetPayout), 
                                              &m_nBalance))
        {
            // all is well, send the client the win info.
            m_pConn->msgStart();
            m_pConn->msgAdd((uint8)'X');
            m_pConn->msgAdd((uint8)(m_nDblUpPayType & 0xFF));
            m_pConn->msgAdd(m_nDblUpPayout);
            m_pConn->msgAdd(m_nBalance);
            m_pConn->msgEnd();

            bResult = true;
        }
        else
        {
            // Hmmmm...something went wrong in the database update...there's no telling
            // what.  Send a generic error message to the user.  But the game is still 
            // over as far as particular game object is concerned.
            m_pConn->Error(LOGINERR_CONTACTCUSTSERVICE);
        }

        m_pConn->gameOver();
    }

    return bResult;
}



//////////////////////////////////////////////////////////////////////////////////////////
/// @fn SevenCardStudGame::deal(int32 nBet, int32 nSideBet, uint8 ucSideBetRank)
/// 
/// This method handles the creation of a new game and the deal of the cards.  Called when
/// the user first hits the "deal" button.
///
/// @param nBet         - (in)  the user's initial wager (in credits)
/// @param nSideBet     - (in)  the user's sidebet (in credits).  Zero for no sidebet.
/// @param nSideBetRank - (in)  the card the user selected for thier side bet.
///
/// @return DEAL_SUCCESS on success, otherwise a value giving some idea of what went wrong.
//////////////////////////////////////////////////////////////////////////////////////////
int32 SevenCardStudGame::deal(int32 nBet, int32 nSideBet, uint8 ucSideBetRank)
{
    int32 nRetVal = DEAL_CREATE_GAME_FAILURE;

    if (m_eState == VPGS_NOTSTARTED)
    {
        if (nBet > 0 && nBet <= m_pConn->getMaxBet())   // make sure that the client is sending a valid bet.
        {
            // first see about dealing new cards for the user
            m_hand.removeAll();

	        Shue *pShue = new Shue(&m_jokerCardMgr, 1, m_sgi);

	        if (pShue)
	        {                
		        pShue->shuffle();

                SevenCardSavedGame savedGame;
                save(&savedGame);   // get the general game state info, we'll be writing it to the database shortly

                // save off the deck seed so that we can re-deal this hand if the game is restored.
                pShue->save(&savedGame.shue);

		        m_hand.add(pShue->getCard());
                m_hand.add(pShue->getCard());
                m_hand.add(pShue->getCard());
                m_hand.add(pShue->getCard());
                m_hand.add(pShue->getCard());
                m_hand.add(pShue->getCard());
                m_hand.add(pShue->getCard());

                delete pShue;

                // save off this info, we'll need it later.
                m_nBet = m_nTotalBet = nBet;                
                m_nSideBet      = nSideBet;                
                m_ucSideBetRank = ucSideBetRank;
                
                // init these values for the new game
                m_nPayout=0;
                m_nBet_2 = 0;
                m_nBet_3 = 0;
                m_nBet_4 = 0;
                m_nBestHandRank = HR_HIGHCARD;

                // more save game info
                savedGame.ucSideBetRank = ucSideBetRank;
                savedGame.nBet          = m_pConn->creditsToCents(nBet);
                savedGame.nSideBet      = m_pConn->creditsToCents(nSideBet);

                // okay, run all this info through the database for final verification and to save game state.
                int nResult = m_pDBInterface->trackNewGame(&savedGame, &m_nBalance);

                if(nResult == VPGameBase::NG_OK)
                {
                    // the database update was successful

                    // update our game state so that we know what step we're on
                    m_nPhase = 1;
                    m_eState = VPGS_GAMESTARTED;                    

                    // build a hand that only shows the 1st and 3rd cards
                    VPHand visible(&m_jokerCardMgr);
                    visible.add(m_hand.getCardAt(0));
                    visible.add(0xFF);
                    visible.add(m_hand.getCardAt(2));
                    visible.add(0xFF);
                    visible.add(0xFF);
                    visible.add(0xFF);
                    visible.add(0xFF);

                    // send initial game data to the client.
                    m_pConn->msgStart();
                    m_pConn->msgAdd((uint8)'D');
                    m_pConn->msgAdd((uint8)(visible.getCount() & 0xFF));
                    m_pConn->msgReleaseBuf(visible.out(m_pConn->msgGetBuf()));
                    m_pConn->msgAdd((uint8)(m_nBestHandRank & 0xFF));
                    m_pConn->msgAdd(m_nBalance);
                    m_pConn->msgEnd();

                    nRetVal = DEAL_SUCCESS;
                }
                else
                {
                    // Hmmmm.  Something went wrong in the database...
                    switch (nResult)
                    {
                        case VPGameBase::NG_NOMONEY:
                        {
                            // The player didn't have enough money to play this game.
                            // Let them know.
                            nRetVal = DEAL_NOT_ENOUGH_MONEY;
                            break;
                        }
                        default:
                        {
                            // something else was wrong...send a generic error.
                            nRetVal = DEAL_CREATE_GAME_FAILURE;
                            break;
                        }
                    }
                }
            }
        }
        else
        {
            // Attempt to make an invalid bet...very suspicious.  Send an error.
            nRetVal = DEAL_INVALID_BET;
        }
    }

    return nRetVal;
}



//////////////////////////////////////////////////////////////////////////////////////////////////
///  @fn    SevenCardStudGame::showNext(int32 nBet)
/// 
/// The player has made the second or third bet and hit "Deal".  Respond accordingly.
///
/// @param nBet -   The amount the player bet.  Must be greater than zero.
///
//////////////////////////////////////////////////////////////////////////////////////////////////
void SevenCardStudGame::showNext(int32 nBet)
{
    if (m_eState == VPGS_GAMESTARTED && m_nPhase<3)
    {
        if(nBet>0 &&
           (m_nPhase==1 && nBet<=m_nBet)   == false &&
           (m_nPhase==2 && nBet<=m_nBet_2) == false)
        {
            // The player must bet more than 0 credits, but less than or equal to the previous bet.
            // Otherwise, something is wrong, so send an error.
            m_pConn->Answer('B');
        }

        // okay, things seem to be kosher, run this though the database for further verification and
        // to update the save game info.
        int32 nResult = m_pDBInterface->trackNextBet(m_nPhase+1, m_pConn->creditsToCents(nBet), &m_nBalance);

        if(nResult == GameDBInterface::Success)
        {
            // we are good to go, move the game to the next phase and send bet results to client.            
            ++m_nPhase;

            m_nTotalBet  += nBet;

            // prepare a hand to send to the client...
            // 0xff out the cards that they're not supposed to see.
            VPHand visible(&m_jokerCardMgr);

            visible.restore(m_hand.getCards(), m_hand.getCount());

            if(m_nPhase==2)
            {
                m_nBet_2 = nBet;
                visible.replace(1,0xff);
                visible.replace(3,0xff);
                visible.replace(5,0xff);
                visible.replace(6,0xff);
            }
            else if(m_nPhase==3)
            {
                m_nBet_3 = nBet;
                visible.replace(1,0xff);
                visible.replace(3,0xff);
                visible.replace(6,0xff);
            }


            // send current game data to the client.
            m_pConn->msgStart();
            m_pConn->msgAdd((uint8)'1');
            m_pConn->msgAdd((uint8)(visible.getCount() & 0xFF));
            m_pConn->msgReleaseBuf(visible.out(m_pConn->msgGetBuf()));
            m_pConn->msgAdd((uint8)(m_nBestHandRank & 0xFF));
            m_pConn->msgAdd(m_nBalance);
            m_pConn->msgEnd();
        }
        else if(nResult == GameDBInterface::NotEnoughMoney)
        {
            // Error.  User out of money.
            m_pConn->Answer('$');
        }
        else
        {
            // Hmmm...unknown error...send generic error message.
            m_pConn->Answer('N');
        }

    }
    else
    {
        // hmmmm.  Maybe we're on the final bet...run this by the show all function.
        showAll(nBet);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////////
/// @fn SevenCardStudGame::showAll(int32 nBet)
///
/// This method is always called when the final bet on the main card game is made, OR if the 
/// player hit the "Deal All" button.  This method calculates the payout for the player's hand,
/// decides whether the sidebet was won, and whether the player gets offered a chance to play 
/// double or nothing.
///
/// If the user doesn't qualify for double or nothing, then this call ends the current game.
///
/// @param nBet -   (in) the amount the player bet.  May be zero if the player hit "Deal All".
/// 
/////////////////////////////////////////////////////////////////////////////////////////////
void SevenCardStudGame::showAll(int32 nBet)
{
    if (m_eState == VPGS_GAMESTARTED && m_nPhase<=4 && nBet>=0)
    {

        // the bet must be less than or equal to the last bet the player made on the current game.
        if((m_nPhase==1 && nBet<=m_nBet)   == false &&
           (m_nPhase==2 && nBet<=m_nBet_2) == false && 
           (m_nPhase==3 && nBet<=m_nBet_3) == false )
        {
            // error.  Invalid bet.
            m_pConn->Answer('B');
        }
        
        uint8 ucDUActive=0;

        int32 nResult = GameDBInterface::Success;

        m_nBet_4    = nBet;
        m_nTotalBet = m_nBet + m_nBet_2 + m_nBet_3 + m_nBet_4;
        

        // Figure out if the player won, and if so, what they won.
        calcPayout();

		//psharpless 10/23/06
		//Case: 11052  New Double Up Win Cap Logic

        if(m_bDblUpEnabled==true && m_nBestHandRank>=HR_STRAIGHT &&
			m_pConn->creditsToCents(m_nPayout) <= m_nDlbUpCapAmount)
        {
            // the user gets to play double or nothing
            ucDUActive=1;
            m_nDblUpPayType = m_nBestHandRank;
            m_nDblUpPayout  = m_nPayout;
        }
        else
        {
            // no double up round for this game.  The game ends on this call.
            ucDUActive=0;
            m_nDblUpPayType = 0;
            m_nDblUpPayout  = 0;
        }

        // run everything through the database for final verification and to update the saved game info.
        nResult = m_pDBInterface->trackShowAll(m_nPhase+1, m_pConn->creditsToCents(nBet),
                                               m_pConn->creditsToCents(m_nPayout), 
                                               m_pConn->creditsToCents(m_nSideBetPayout),
                                               &m_hand, m_aucBestCards, m_nBestHandRank, 
                                               m_nSideBetWinType, ucDUActive, &m_nBalance);

        if(nResult == GameDBInterface::Success)
        {
            // All is well.  Update our current state info and alert the client.
            m_nPhase     = 4;
            m_nBet_4     = nBet;

            if(ucDUActive)
            {
                // game is still active...we're waiting to hear from the user
                // about whether they want to play double up.
                m_eState = VPGS_DBLUPPENDING;
            }
            else
            {
                // this game is OVER.
                m_eState = VPGS_GAMEOVER;
            }

            // send back the 5 cards making up the best hand.  We'll grey out the other 2 cards.
            VPHand bestCards(&m_jokerCardMgr);

            if(m_nBestHandRank>=0)
            {
                bestCards.restore(m_aucBestCards, 5);
            }
            else
            {
                // kharmon.  03-31-06.  #10886.  Don't highlight the best 5 cards if
                // the player didn't win anything.
                bestCards.add(0xFF);
                bestCards.add(0xFF);
                bestCards.add(0xFF);
                bestCards.add(0xFF);
                bestCards.add(0xFF);
            }

            // send all relevant data back to the client.
            m_pConn->msgStart();
            m_pConn->msgAdd((uint8)'A');
            m_pConn->msgAdd((uint8)(m_hand.getCount() & 0xFF));
            m_pConn->msgReleaseBuf(m_hand.out(m_pConn->msgGetBuf()));
            m_pConn->msgAdd((uint8)(bestCards.getCount() & 0xFF));
            m_pConn->msgReleaseBuf(bestCards.out(m_pConn->msgGetBuf()));
            m_pConn->msgAdd((uint8)(m_nBestHandRank & 0xFF));
            m_pConn->msgAdd(m_nPayout);
            m_pConn->msgAdd(ucDUActive);
            m_pConn->msgAdd((uint8)(m_nDblUpPayType & 0xFF));
            m_pConn->msgAdd(m_nDblUpPayout);
            m_pConn->msgAdd((uint8)(m_nSideBetWinType & 0xFF));
            m_pConn->msgAdd(m_nSideBetPayout);
            m_pConn->msgAdd(m_nBalance);
            m_pConn->msgEnd();
        }
        else if(nResult == GameDBInterface::NotEnoughMoney)
        {
            // user doesn't have enough money to make this bet.            
            m_pConn->Answer('$');
            m_nBet_4 = 0;   // reset this to zero since we failed.
        }
        else
        {
            // hmmmm...something went wrong.  Alert the user.
            m_pConn->Answer('N');
            m_nBet_4 = 0;   // reset this to zero since we failed.
        }
    }
    else
    {
        if(nBet<0)
        {
            // invalid bet from client...suspicous.
            m_pConn->Answer('B');
        }
        else
        {
            // The client is sending invalid messages for the
            // current game state.  Send back generic error.
            m_pConn->Answer('N');
        }
    }
}


////////////////////////////////////////////////////////////////////////////////
/// @fn SevenCardStudGame::initOk()
///
/// Checks the game state to make sure that it's valid.  Basically just checks
/// some obvious stuff.
///
////////////////////////////////////////////////////////////////////////////////
bool8 SevenCardStudGame::initOk()
{
    bool8 bRetVal = true;

    // make sure that each bet is less than or equal to the previous bet.
    if(m_nBet_4>m_nBet_3 || m_nBet_3>m_nBet_2 || m_nBet_2>m_nBet)
    {
        bRetVal = false;
    }

    // make sure that the best hand rank is in the valid range.
    if(m_nBestHandRank<HR_HIGHCARD || m_nBestHandRank > HR_ROYALFLUSH)
    {
        bRetVal = false;
    }

    // 8 is the largest valid value for m_ucPayTable
    if(m_ucPayTable>8)
    {
        bRetVal = false;
    }

    // only phases 0 thru 4 is valid.
    if(m_nPhase<0 || m_nPhase>4)
    {
        bRetVal = false;
    }

    return bRetVal;
}

/////////////////////////////////////////////////////////////////////////////////////
/// @fn SevenCardStudGame::doubleUpDeal()
/// 
/// Called when the user accepts the double or nothing option.
///
/// @return true if all is well, false otherwise.
/////////////////////////////////////////////////////////////////////////////////////
bool8 SevenCardStudGame::doubleUpDeal()
{
    bool8 bResult = false;

    if (m_eState == VPGS_DBLUPPENDING)
    {
	    Shue *pShue = new Shue(&m_stdCardMgr, 1, m_sgi);

	    if (pShue)
	    {
            // pull the cards out of the shue and assign them in the correct order.
		    pShue->shuffle();

            m_dblUpHand.restore(NULL,0);

            ShueSave shueSave;
            pShue->save(&shueSave); // save off the shue seed so that we can restore the game later and get this same hand.

            // get the hidden "big/small" card 
            m_ucDblUpCard = pShue->getCard();
            m_nDblUpRank  = m_stdCardMgr.getRank(m_ucDblUpCard);

		    m_dblUpHand.add(0xFF);  // place holder for the "big/small" card.
            m_dblUpHand.add(pShue->getCard());  // get the visible cards.
            m_dblUpHand.add(pShue->getCard());
            m_dblUpHand.add(pShue->getCard());
            m_dblUpHand.add(pShue->getCard());
            m_dblUpHand.add(pShue->getCard());
            m_dblUpHand.add(pShue->getCard());

            delete pShue;

            // update the database
            if (m_pDBInterface->trackDoubleUpDeal(m_nDblUpRound,
                                                  &shueSave,
                                                  &m_nBalance))
            {
                // all is well.  Send the data and wait for the player's big/small guess.
                m_eState = VPGS_DBLUPSTARTED;

                m_pConn->msgStart();
                m_pConn->msgAdd((uint8)'U');
                m_pConn->msgAdd((uint8)(m_dblUpHand.getCount() & 0xFF));
                m_pConn->msgReleaseBuf(m_dblUpHand.out(m_pConn->msgGetBuf()));
                m_pConn->msgAdd(m_nBalance);
                m_pConn->msgEnd();

                bResult = true;
            }
        }
    }

    return bResult;
}


///////////////////////////////////////////////////////////////////////////////////////
/// @fn  SevenCardStudGame::doubleUpSelect(uint8 ucBigSmall)
///
/// This is called when the user has made their big/small guess on the double or nothing
/// round.  If they guessed right, they get another double up round.  If they guessed
/// wrong, their game ends here.
///
/// @param ucBigSmall   -   (in)  0 if they guessed small, 1 if they guessed big.
///
/// @return DUDRAW_SUCCESS_GAMEOVER if they guessed wrong, DUDRAW_SUCCESS_DBLUP_PENDING
/// if they guessed right, and DUDRAW_FAILURE if something unexpected happened.
///////////////////////////////////////////////////////////////////////////////////////
int32 SevenCardStudGame::doubleUpSelect(uint8 ucBigSmall)
{
    int32 nResult = DUDRAW_FAILURE;

    if(m_eState == VPGS_DBLUPSTARTED)
    {
        int32 nRound = m_nDblUpRound;
        uint8 ucWinResult = 'W';

        // if the player guessed big and the rank was greater than 7, but not an ace,
        // OR the player guessed small and the rank was less than 7, OR an ace, then
        // the player wins.  A 7 always loses, regardless of what the player guessed.
        if((ucBigSmall==1 && m_nDblUpRank>7 && m_nDblUpRank!=14)  || 
           (ucBigSmall==0 && (m_nDblUpRank<7 || m_nDblUpRank==14)))
        {
            // Player wins.  Update state info and prepare for the next
            // round.
            m_nDblUpPayout = m_nDblUpPayout << 1;        
            m_nDblUpPayType = VPEVT_DBLUP_WON;
            ++m_nDblUpRound;
            ++m_nDblUpWon;

			//psharpless 10/23/06
			//Case: 11052  New Double Up Win Cap Logic
            if(m_nDblUpRound>999 ||
			   m_pConn->creditsToCents(m_nDblUpPayout) > m_nDlbUpCapAmount)
            {
                // don't allow more than 999 double up rounds.  Ack.
                nResult = DUDRAW_SUCCESS_GAMEOVER;
                m_eState = VPGS_GAMEOVER;
            }
            else
            {
                nResult = DUDRAW_SUCCESS_DBLUP_PENDING;
                m_eState = VPGS_DBLUPPENDING;
            }
        }
        else
        {
            // House wins.  This game is over.  Set state information accordingly.
            m_nDblUpPayout = 0;                        
            m_nDblUpPayType = VPEVT_DBLUP_LOSER;
            m_nDblUpRound = 0;
            m_nDblUpWon = 0;
            nResult = DUDRAW_SUCCESS_GAMEOVER;
            m_eState = VPGS_GAMEOVER;
            ucWinResult = 'L';
        }

        // put the "Big/Small" card back into the hand as the first card so that
        // we have it recorded in the database, for reporting purposes.
        m_dblUpHand.replace(0,m_ucDblUpCard);

        // update the database with latest results.  If the user lost, this ends the game.
        if(m_pDBInterface->trackDoubleUpSelect(nRound, &m_dblUpHand, ucBigSmall, ucWinResult, 
                                               m_pConn->creditsToCents(m_nDblUpPayout),                                               
                                               &m_nBalance))
        {
            // send the "big/small" card down to the client for display, and also
            // send the win/loss info.
            VPHand hand(&m_stdCardMgr);
            hand.add(m_ucDblUpCard);

            m_pConn->msgStart();
            m_pConn->msgAdd((uint8) 'u');
            m_pConn->msgAdd((uint8) 1);
            m_pConn->msgReleaseBuf(hand.out(m_pConn->msgGetBuf()));
            m_pConn->msgAdd((uint8)(m_eState == VPGS_DBLUPPENDING));
            m_pConn->msgAdd((uint8)(m_nDblUpPayType & 0xFF));
            m_pConn->msgAdd((uint8)(m_nDblUpWon & 0xFF));
            m_pConn->msgAdd(m_nDblUpPayout);
            m_pConn->msgAdd(m_nBalance);
            m_pConn->msgEnd();

			//psharpless 10/23/06
			//Case: 11052  New Double Up Win Cap Logic

			//we just won a double up, but another double up is not pending, 
			//we must have hit the cap so update the users balance with their winnings
			if(m_nDblUpWon && ! (m_eState & VPGS_DBLUPPENDING))
			{
				m_pDBInterface->trackDoubleUpDeny(m_pConn->creditsToCents(m_nDblUpPayout),
																		  m_pConn->creditsToCents(m_nSideBetPayout),
																		  &m_nBalance);
			}
        }
        else
        {
            // not good.  Alert the user to the error and 
            // close down the game object.
            nResult = DUDRAW_FAILURE;
        }
    }

    return nResult;
}



///////////////////////////////////////////////////////////////////////////////////////////
/// @fn  SevenCardStudGame::calcPayout()
///
/// This method figures out if the player won anything, and if so, how much.  Also
/// figures out how they won and saves that info in class variables m_nBestHandRank,
/// m_bestHand, m_aucBestCards, and m_nSideBetWinType.
/// 
///////////////////////////////////////////////////////////////////////////////////////////
void SevenCardStudGame::calcPayout()
{
    // find the best hand that can be made out of the seven cards that the player was dealt.
    m_nBestHandRank = findBestHand(m_hand, m_bestHand, m_aucBestCards);

    // go to the paytable to see what, if anything, the player won.
    m_nPayout = m_nTotalBet * m_anPayTable[m_ucPayTable][m_nBestHandRank + 1];

    if(m_nPayout==0)
    {
        // if there was no payout, then this isn't a win for the current
        // paytable, so just write it as a loss (no paytables pay out for high card).
        m_nBestHandRank = HR_HIGHCARD;
    }
    else if(m_nBestHandRank==HR_2PAIR)
    {
        // kharmon.  04-07-06.  #11095.  To win, 2 pair must have one pair be at least 10s.
        m_bestHand.calcNthHighest(0);
        if(m_bestHand.getHighCard()<10)
        {
            // Close, but no good.  This hand is a loser.
            m_nPayout=0;
            m_nBestHandRank = HR_HIGHCARD;
        }
    }

    // now let's look at the side bet
    m_nSideBetPayout = 0;
    m_nSideBetWinType = HR_HIGHCARD;

    // side bet must be enabled, the player must have placed a side bet, and must have
    // made all 4 wagers, AND the best hand must be better than 4 of a kind in their
    // best hand so far.
    if(m_bSideBetEnabled!=false && m_nSideBet>0 && m_nBestHandRank>=HR_4OFAKIND && 
       m_nBet>0 && m_nBet_2>0 && m_nBet_3>0 && m_nBet_4>0)
    {              
        // SO, the player made it through the initial checks, but we need to do more
        // analsys.  The player MUST have a NATURAL 4 of a kind or better...no jokers.
        // AND, that natural hand must contain the card that the player picked when 
        // they made the side bet.
        bool8 bHasSideBetCard = false;

        VPHand noJokers(&m_stdCardMgr);

        // get all of the no-joker cards and see if we have the side bet card.
        for(int nI=0;nI<7; ++nI)
        {
            uint8 ucCard = m_hand.getCardAt(nI);            

            if(m_jokerCardMgr.getSuit(ucCard)!=JokerCardMgr::SPECIAL)
            {
                noJokers.add(ucCard);
            }

            if(m_jokerCardMgr.getRank(ucCard)==m_ucSideBetRank)
            {
                bHasSideBetCard = true;
            }
        }        

        if(bHasSideBetCard)
        {
            // okay...the 7 card hand had the side bet card...BUT, is that card part 
            // of a winning 5-card hand, AND is that winning hand a "natural" (no joker) hand.
            FiveCardHand bestNaturalHand(&m_stdCardMgr);

            m_nSideBetWinType = findBestNoJokerHand(noJokers, bestNaturalHand);

            if(m_nSideBetWinType == HR_4OFAKIND)
            {
                bestNaturalHand.calcNthHighest(0);
                if(bestNaturalHand.getHighCard() != m_ucSideBetRank)
                {
                    m_nSideBetWinType = HR_HIGHCARD;
                }
            }
        }


        if(bHasSideBetCard && m_nSideBetWinType >= HR_4OFAKIND)
        {
            // the user made a side bet and is in the money.            
            m_nSideBetPayout = (int32) floor(m_anSideBetPayTable[m_ucSideBetPayTable][m_nSideBetWinType+1] + 
                                             m_anSideBetPayTable[m_ucSideBetPayTable][m_nSideBetWinType+1] * m_nTotalBet * 0.1); // #10747.  The player never wins the base amount...even the first credit adds 10%.
        }
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn SevenCardStudGame::findBestNoJokerHand(VPHand& noJokerHand, FiveCardHand& bestHand, uint8 aucBestCards[])
///
/// This method takes all of the non-joker cards in the original 7 card hand and looks for the best possible
/// hand out of those cards that ALSO contains the sidebet card.
///
/// @param noJokerHand  -   (in)  All of the non-joker cards in the original hand.
/// @param bestHand     -   (out) used to return the best possible hand.
///
/// @return the rank of the best hand that we find
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int32 SevenCardStudGame::findBestNoJokerHand(VPHand& noJokerHand, FiveCardHand& bestHand)
{
    int32 nBestHandRank = -2;
    bestHand.restore(NULL,0);

    uint8 aucBestCards[5];
    memset(aucBestCards, 0, sizeof(aucBestCards));

    int nCardCnt = noJokerHand.getCount();

    VPHand testHand(&m_jokerCardMgr);
    VPHand baseHand(&m_jokerCardMgr);

    bool8 bHasSideBetCard = false;

    // get the first 5 cards and see if the side bet card is one of them.
    for(int nI=0;nI<5; ++nI)
    {
        uint8 ucCard = noJokerHand.getCardAt(nI);
        baseHand.add(ucCard);

        if(m_stdCardMgr.getRank(ucCard)==m_ucSideBetRank)
        {
            bHasSideBetCard=true;
        }
    }

    if(bHasSideBetCard==true)
    {
        // if one of the first 5 cards (the "base" hand) was the side bet card,
        // get the hand rank.
        testHand.restore(baseHand.getCards(), baseHand.getCount());

        evaluateRank(testHand, nBestHandRank, bestHand, aucBestCards);
    }


    // if we had more than 5 non-joker cards, then test all 5 card combinations 
    // of the base hand plus the 6th card.
    if(nCardCnt>5)
    {
        for(nI=0;nI<5;++nI)
        {
            testHand.restore(baseHand.getCards(), baseHand.getCount());
            testHand.replace(nI,noJokerHand.getCardAt(5));

            // ONLY test the 5 card hands that contain the side bet card.
            for(int nJ=0;nJ<5; ++nJ)
            {                
                if(m_stdCardMgr.getRank(testHand.getCardAt(nJ))==m_ucSideBetRank)
                {
                    // aha, this 5 card hand does have the side bet card...test it.
                    evaluateRank(testHand, nBestHandRank, bestHand, aucBestCards);
                    break;
                }
            }
        }
    }
    
    if(nCardCnt>6)
    {
        // hmmmm...there were no jokers in this hand...all 7 cards were good.

        // SO, now do all 5 card combinations that contain the 7th card.
        for(nI=0;nI<5;++nI)
        {
            testHand.restore(baseHand.getCards(), baseHand.getCount());
            testHand.replace(nI,noJokerHand.getCardAt(6));

            // only test the 5 card hands that contain the side bet card.
            for(int nJ=0;nJ<5; ++nJ)
            {                
                if(m_stdCardMgr.getRank(testHand.getCardAt(nJ))==m_ucSideBetRank)
                {
                    // this 5 card hand has the side bet card...test it.
                    evaluateRank(testHand, nBestHandRank, bestHand, aucBestCards);
                    break;
                }
            }
        }

        // And now do all 5 card combinations that contain both of the extra 2 cards.
        for(nI=0;nI<4;++nI)
        {
            for(int nJ=nI+1;nJ<5;++nJ)
            {
                testHand.restore(baseHand.getCards(), baseHand.getCount());
                testHand.replace(nI,noJokerHand.getCardAt(5));
                testHand.replace(nJ,noJokerHand.getCardAt(6));

                // only test the 5 card hands that contain the side bet card.
                for(int nZ=0;nZ<5; ++nZ)
                {
                    if(m_stdCardMgr.getRank(testHand.getCardAt(nZ))==m_ucSideBetRank)
                    {
                        // this 5 card hand has the side bet card...test it.
                        evaluateRank(testHand, nBestHandRank, bestHand, aucBestCards);
                        break;
                    }
                }
            }
        }
    }

    return nBestHandRank;
}



/////////////////////////////////////////////////////////////////////////////////////////////
/// @fn SevenCardStudGame::save(SevenCardSavedGame* pSaved)
///
/// Fills the pSaved structure with some basic game state info.
///
/// @param pSaved   -   (out)  holds basic game state data
///
/////////////////////////////////////////////////////////////////////////////////////////////
void SevenCardStudGame::save(SevenCardSavedGame* pSaved)
{   
    memset(pSaved,0,sizeof(*pSaved));
    pSaved->ucDblUpEnabled      = m_bDblUpEnabled;
    pSaved->ucPayTable          = m_ucPayTable;
    pSaved->ucSideBetPayTable   = m_ucSideBetPayTable;
    pSaved->ucDeckConfiguration = m_ucDeckConfiguration;
    pSaved->bSideBetEnabled     = m_bSideBetEnabled;
}


/////////////////////////////////////////////////////////////////////////////////////////////
/// @fn void SevenCardStudGame::evaluateRank(VPHand &testHand, int32& nBestHandRank, 
///                                          FiveCardHand &bestHand, uint8 aucBestCards[])
///
/// Takes testHand, which may include multiple Jokers, and figures out the best 
/// possible hand rank.
///
/// @param  testHand      -   (in)     The hand we will be testing.
/// @param  nBestHandRank -   (in/out) We compare the ranks that we find with this, and if the
///                                    new ranks are better, we replace this value as well as
///                                    bestHand and aucBestCards with the new data.
/// @param  bestHand      -   (out)    If we find a better hand than nBestHandRank, we'll put
///                                    the new best hand in here, with the jokers replaced by 
///                                    what they're substituting for.
/// @param  aucBestCards  -   (out)    If we find a better hand than nBestHandRank, we'll put
///                                    the new best cards (including jokers) in here.
///
/////////////////////////////////////////////////////////////////////////////////////////////
void SevenCardStudGame::evaluateRank(VPHand &testHand, int32& nBestHandRank, 
                                     FiveCardHand &bestHand, uint8 aucBestCards[])
{    
    uint8 aucJokerCounts[5];
    uint8 aucJokerStack[5];
    int nTop = -1;
    
    uint8 aucWorking[5];

    uint8 *pucOrig = testHand.getCards();

    memcpy(aucWorking, pucOrig, 5);

    memset(aucJokerCounts,0,sizeof(aucJokerCounts));

    FiveCardHand hand(&m_stdCardMgr);

    // go through the hand looking for jokers.  We expect a max of 5 cards.
    for(int nI=0;nI<5;++nI)
    {
        uint8 ucCard = pucOrig[nI];

        if(m_jokerCardMgr.getSuit(ucCard)==JokerCardMgr::SPECIAL)
        {
            // put the joker position on the stack and increment the stack pointer.
            aucJokerStack[++nTop] = nI;
        }
    }
    
    if(nTop>=0)
    {
        // IF testHand contained one or more jokers:

        // The basic idea is that we'll test all possible hands by substituting 
        // in all 52 basic cards for this joker, and testing each resulting hand.
        // We keep the best hand we find.  Not elegant, but safe and easy to implement.
        bool bDone;

        // while we still have joker replacements to test:
        do
        {
            bDone=true; // always start by assuming that this is our last pass

            // start with the original cards, including jokers.
            memcpy(aucWorking, pucOrig, 5);            


            // replace all the jokers with the appropriate cards
            for(nI=0;nI<=nTop;++nI)
            {
                int     nPos   = aucJokerStack[nI];
                uint8   ucCard = m_stdCardMgr.makeCard(aucJokerCounts[nPos]);

                aucWorking[nPos] = ucCard;
            }

            // move our joker-free working hand into the object we'll use to do the test.
            hand.restore(aucWorking, 5);

            hand.rankHand();    // determine the hand rank

            int nRank = hand.getRank();

            if(nRank > nBestHandRank)
            {
                // if this new hand is better than our best hand so far, replace
                // our old best hand with this new hand.
                nBestHandRank = nRank;
                bestHand.restore(aucWorking, 5);
                bestHand.rankHand();
                memcpy(aucBestCards, testHand.getCards(),5);
            }
            else if(nRank==nBestHandRank)
            {              
                // if the old best hand and this new hand have the same rank,
                // test who has the highest cards.
                for (int nZ = 0; nZ < 5; ++nZ)
                {
                    hand.calcNthHighest(nZ);
                    bestHand.calcNthHighest(nZ);

                    if (hand.getHighCard() > bestHand.getHighCard())
                    {
                        // the new hand has a higher card at this level, so replace
                        // the old best hand with the new hand.
                        bestHand.restore(aucWorking,5);
                        bestHand.rankHand();
                        memcpy(aucBestCards, testHand.getCards(),5);
                        break;
                    }
                    else if (hand.getHighCard() < bestHand.getHighCard())
                    {
                        // the old best hand has a higher card...stop looking.
                        break;
                    }
                }
            }

            // okay, now update our joker replacement info so that
            // we do the next hand combination
            for(nI=nTop;nI>=0;--nI)
            {
                int nPos = aucJokerStack[nI];

                ++aucJokerCounts[nPos]; // increment the replacement card for the current joker

                if(aucJokerCounts[nPos]>51)
                {
                    // we've gone to the max card on this joker, reset it to
                    // the start and fall back to increment the previous joker
                    // on the next loop
                    aucJokerCounts[nPos]=0;
                }
                else
                {
                    // we haven't tried EVERY replacement card on this joker yet
                    // so leave the other jokers alone and keep testing.
                    bDone=false;
                    break;
                }
            }
        }
        while(bDone==false); // keep looping until we've tested every possible variation.
    }
    else
    {
        // This hand doesn't contain any jokers!  Excellent, much simpler.
        hand.restore(pucOrig, 5);

        // get the hand's rank
        hand.rankHand();        
        int nRank = hand.getRank();

        if(nRank > nBestHandRank)
        {
            // if this rank is better than the previous best hand, then
            // replace the previous data with this new hand's data
            nBestHandRank = nRank;
            bestHand.restore(pucOrig, 5);
            bestHand.rankHand();
            memcpy(aucBestCards, pucOrig, 5);
        }
        else if(nRank==nBestHandRank)
        {                    
            // hmmm...the hands have the same rank...go to who has the high card
            for (int nZ = 0; nZ < 5; ++nZ)
            {
                hand.calcNthHighest(nZ);
                bestHand.calcNthHighest(nZ);

                if (hand.getHighCard() > bestHand.getHighCard())
                {
                    // the new hand has the highest card, and thus is the best hand
                    // so replace the previous best data with our new data
                    bestHand.restore(pucOrig,5);
                    bestHand.rankHand();
                    memcpy(aucBestCards, pucOrig, 5);
                    break;
                }
                else if (hand.getHighCard() < bestHand.getHighCard())
                {
                    // the old hand has a higher rank...break.
                    break;
                }
            }
        }
    }    
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn SevenCardStudGame::findBestHand(VPHand& startingHand, FiveCardHand& bestHand, uint8 aucBestCards[])
///
/// This method takes the starting 7 card hand and finds the best 5 card hand that can be had from it,
/// taking jokers into account.
///
/// @param startingHand -   (in)  the 7 cards we were dealt
/// @param bestHand     -   (out) the best 5 card hand, with jokers replaced by the cards they're substituting for.
/// @param aucBestCards -   (out) the 5 cards that make up the best hand, with jokers left intact (unsubstituted).
///
/// @return the value indicating the rank of the best 5 card hand.
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
int32 SevenCardStudGame::findBestHand(VPHand& startingHand, FiveCardHand& bestHand, uint8 aucBestCards[])
{
    int32 nBestHandRank = -2;

    bestHand.restore(NULL,0);

    VPHand testHand(&m_jokerCardMgr);
    VPHand baseHand(&m_jokerCardMgr);

    // move the first 5 cards into our "base" hand.
    for(int nI=0;nI<5; ++nI)
    {
        baseHand.add(startingHand.getCardAt(nI));
    }

    // test the base hand to get it's rank.
    testHand.restore(baseHand.getCards(), baseHand.getCount());
    evaluateRank(testHand, nBestHandRank, bestHand, aucBestCards);

    // now, swap in the 6th card for each of the 5 base cards, and test each resulting hand.
    for(nI=0;nI<5;++nI)
    {
        testHand.restore(baseHand.getCards(), baseHand.getCount());
        testHand.replace(nI,startingHand.getCardAt(5));

        evaluateRank(testHand, nBestHandRank, bestHand, aucBestCards);
    }

    // now, swap in the 7th card for each of the 5 base cards, and test each resulting hand.
    for(nI=0;nI<5;++nI)
    {
        testHand.restore(baseHand.getCards(), baseHand.getCount());
        testHand.replace(nI,startingHand.getCardAt(6));

        evaluateRank(testHand, nBestHandRank, bestHand, aucBestCards);
    }

    // NOW, swap BOTH the 6th and 7th card into the base hand, thus creating all 
    // possible hand combinations.
    for(nI=0;nI<4;++nI)
    {
        for(int nJ=nI+1;nJ<5;++nJ)
        {
            testHand.restore(baseHand.getCards(), baseHand.getCount());
            testHand.replace(nI,startingHand.getCardAt(5));
            testHand.replace(nJ,startingHand.getCardAt(6));

            evaluateRank(testHand, nBestHandRank, bestHand, aucBestCards);
        }
    }

    // having tested all 5 card hands, we now have the best possible hand. 
    // return the rank.
    return nBestHandRank;
}


///////////////////////////////////////////////////////////////////////////////////////
/// @fn SevenCardStudGame::send()
///
/// This method sends the restored game state information down to the client so that 
/// the player can complete an unfinished game.
///
///////////////////////////////////////////////////////////////////////////////////////
void SevenCardStudGame::send()
{
    switch(m_eState)
    {
        case VPGS_GAMESTARTED:
        {
            // the player was still in the main part of the game.
            m_pConn->msgAdd((uint8)0x00);
            break;
        }
        case VPGS_DBLUPPENDING:
        {
            // the user was being asked if they wanted to play double or nothing 
            // restore them to this state.
            m_pConn->msgAdd((uint8)0x01);    

            break;
        }
        case VPGS_DBLUPSTARTED:
        {
            // The player was being asked to pick, big or small.
            // restore them to this state.
            m_pConn->msgAdd((uint8)0x02);    

            break;
        }
        default:
        {
            return;
        }
    }

    // data common to all states:
    m_pConn->msgAdd(m_ucPayTable);
    m_pConn->msgAdd(m_bSideBetEnabled);
    m_pConn->msgAdd(m_ucSideBetPayTable);
    m_pConn->msgAdd(m_nSideBet);
    m_pConn->msgAdd(m_nSideBetPayout);
    m_pConn->msgAdd(m_nSideBetWinType);
    m_pConn->msgAdd(m_ucSideBetRank);
    
    m_pConn->msgAdd(m_nBet);
    m_pConn->msgAdd(m_nBet_2);
    m_pConn->msgAdd(m_nBet_3);
    m_pConn->msgAdd(m_nBet_4);

    // state specific data:
    if (m_eState == VPGS_GAMESTARTED)
    {
        // main game in progress state data
        VPHand visible(&m_jokerCardMgr);

        if(m_nPhase==1)
        {
            // the player had made the first bet and seen the initial two cards,
            // so send those two down again.
            visible.add(m_hand.getCardAt(0));
            visible.add(0xFF);
            visible.add(m_hand.getCardAt(2));
            visible.add(0xFF);
            visible.add(0xFF);
            visible.add(0xFF);
            visible.add(0xFF);
        }
        else if(m_nPhase==2)
        {
            // the player had made the second bet and seen the next card
            visible.restore(m_hand.getCards(),7);
            visible.replace(1,0xff);
            visible.replace(3,0xff);
            visible.replace(5,0xff);
            visible.replace(6,0xff);
        }
        else if(m_nPhase==3)
        {
            // the player had made the third bet and seen the next card
            visible.restore(m_hand.getCards(),7);
            visible.replace(1,0xff);
            visible.replace(3,0xff);
            visible.replace(6,0xff);
        }
        else if(m_nPhase==4)
        {
            // the player had made all 4 bets and seen all of the cards
            // (should never hit this code...)
            visible.restore(m_hand.getCards(),7);
        }
        
        m_pConn->msgAdd((uint8)(m_nBestHandRank & 0xFF));
        m_pConn->msgAdd((uint8)(visible.getCount() & 0xFF));
        m_pConn->msgReleaseBuf(visible.out(m_pConn->msgGetBuf()));

        m_pConn->msgAdd((uint8)(m_nPhase & 0xFF));
    }
    else
    {
        // double up state data
        m_pConn->msgAdd((uint8)(m_nBestHandRank & 0xFF));
        m_pConn->msgAdd((uint8)(m_hand.getCount() & 0xFF));
        m_pConn->msgReleaseBuf(m_hand.out(m_pConn->msgGetBuf()));

        m_pConn->msgAdd((uint8)(m_nDblUpPayType & 0xFF));
        m_pConn->msgAdd((uint8)(m_nDblUpWon & 0xFF));
        m_pConn->msgAdd(m_nDblUpPayout);

        VPHand bestCards(&m_jokerCardMgr);
        bestCards.restore(m_aucBestCards, 5);

        m_pConn->msgAdd((uint8)(bestCards.getCount() & 0xFF));
        m_pConn->msgReleaseBuf(bestCards.out(m_pConn->msgGetBuf()));

        if(m_dblUpHand.getCount()>0)
        {
            m_pConn->msgAdd((uint8)(m_dblUpHand.getCount() & 0xFF));
            m_pConn->msgReleaseBuf(m_dblUpHand.out(m_pConn->msgGetBuf()));
        }
    }
}